/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_CONTAINER_DISPATCHER_HPP
#define	_CUBEFS_CORE_CONTAINER_DISPATCHER_HPP

#include <cubefs/core/container/profile.hpp>
#include <cubefs/core/container/builder.hpp>
#include <cubefs/core/container/names.hpp>
#include <cubefs/core/container/type_locator.hpp>

#include <cubefs/core/tools/types/typelist.hpp>

#define CUBEFS_SOURCE "dispatcher.hpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {

using cubefs::TypeList;

template <typename Profile, typename PageType, typename List>
class PageDispatcher {

    typedef typename PageType::ID                                          		PageId;

    typedef typename List::Head                                                 ModelType;
    typedef typename ModelType::Types::Pages::NodeDispatcher                    NodeDispatcher;
    typedef typename ModelType::Types::NodeBase                                 NodeBase;

public:
    void dispatch(PageType *page) {
        if (page->model_hash() == ModelType::hash()) {
            NodeDispatcher::dispatch(static_cast<NodeBase*>(page));
        }
        else {
            PageDispatcher<Profile, PageType, typename List::Tail>::dispatch(page);
        }
    }
};

template <typename Profile, typename PageType>
class PageDispatcher<Profile, PageType, cubefs::NullType> {

public:
    void dispatch(PageType *page) {
        throw DispatchException(CUBEFS_SOURCE, "Invalid model hash code", page->model_hash());
    }
};


template <
        typename Profile,
        typename PageMgr,
        typename List1,
        typename List2 = List1,
        typename List3 = List1,
        typename List4 = List1
>
class ModelDispatcher {
    typedef typename List1::Head Head1;
    typedef typename List1::Tail Tail1;

    typedef typename List2::Head Head2;
    typedef typename List2::Tail Tail2;

    typedef typename List3::Head Head3;
    typedef typename List3::Tail Tail3;

    typedef typename List4::Head Head4;
    typedef typename List4::Tail Tail4;

    typedef typename PageMgr::Page                                              Page;

public:
    template <typename Handler>
    void dispatch_same(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {
        if (root1->model_hash() == Head1::hash()) {
            Head1 model1(*mgr);
            model1.root() = root1->id();

            Head1 model2(*mgr);
            model2.root() = root2->id();

            functor(mgr, model1, model2);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1>::dispatch_same(mgr, root1, root2, functor);
        }
    }

    template <typename Handler>
    void dispatch_same(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {
        if (root1->model_hash() == Head1::hash()) {
            Head1 model1(*mgr);
            model1.root() = root1->id();

            Head1 model2(*mgr);
            model2.root() = root2->id();

            Head1 model3(*mgr);
            model3.root() = root3->id();

            functor(mgr, model1, model2, model3);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1>::dispatch_same(mgr, root1, root2, root3, functor);
        }
    }


    template <typename Handler>
    void dispatch_same(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page* root4, Handler &functor) {
        if (root1->model_hash() == Head1::hash()) {
            Head1 model1(*mgr);
            model1.root() = root1->id();

            Head1 model2(*mgr);
            model2.root() = root2->id();

            Head1 model3(*mgr);
            model3.root() = root3->id();

            Head1 model4(*mgr);
            model4.root() = root4->id();

            functor(mgr, model1, model2, model3, model4);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1>::dispatch_same(mgr, root1, root2, root3, root4, functor);
        }
    }


    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root, Handler &functor) {
        if (root->model_hash() == Head1::hash()) 
        {
            Head1 model(*mgr);
            model.root() = root->id();

            functor(mgr, model);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1>::dispatch(mgr, root, functor);
        }
    }
    
    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {
        if (root1->model_hash() == Head1::hash()) {
            dispatch2_2(mgr, root1, root2, functor);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1, List1>::dispatch(mgr, root1, root2, functor);
        }
    }

    template <typename Handler>
    void dispatch2_2(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {
        if (root2->model_hash() == Head2::hash())
        {
            Head1 model1(*mgr);
            model1.root() = root1->id();

            Head2 model2(*mgr);
            model2.root() = root2->id();

            functor(mgr, model1, model2);
        }
        else {
            ModelDispatcher<Profile, PageMgr, List1, Tail2>::dispatch2_2(mgr, root1, root2, functor);
        }
    }




    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {
        if (root1->model_hash() == Head1::hash()) {
            dispatch3_2(mgr, root1, root2, root3, functor);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1, List1>::dispatch(mgr, root1, root2, root3, functor);
        }
    }

    template <typename Handler>
    void dispatch3_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {
        if (root2->model_hash() == Head2::hash()) {
            dispatch3_3(mgr, root1, root2, root3, functor);
        }
        else {
            ModelDispatcher<Profile, PageMgr, List1, Tail2>::dispatch3_2(mgr, root1, root2, root3, functor);
        }
    }

    template <typename Handler>
    void dispatch3_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {
        if (root3->model_hash() == Head3::hash()) {
            Head1 model1(*mgr);
            model1.root() = root1->id();

            Head2 model2(*mgr);
            model2.root() = root2->id();

            Head3 model3(*mgr);
            model3.root() = root3->id();

            functor(mgr, model1, model2, model3);
        }
        else {
            ModelDispatcher<Profile, PageMgr, List1, List2, Tail3>::dispatch3_3(mgr, root1, root2, root3, functor);
        }
    }



    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {
        if (root1->model_hash() == Head1::hash()) {
            dispatch4_2(mgr, root1, root2, root3, root4, functor);
        }
        else {
            ModelDispatcher<Profile, PageMgr, Tail1, List1>::dispatch(mgr, root1, root2, root3, root4, functor);
        }
    }

    template <typename Handler>
    void dispatch4_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page root4, Handler &functor) {
        if (root2->model_hash() == Head2::hash()) {
            dispatch4_3(mgr, root1, root2, root3, root4, functor);
        }
        else {
            ModelDispatcher<Profile, PageMgr, List1, Tail2>::dispatch4_2(mgr, root1, root2, root3, root4, functor);
        }
    }

    template <typename Handler>
    void dispatch4_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {
        if (root3->model_hash() == Head3::hash()) {
            dispatch4_4(mgr, root1, root2, root3, root4, functor);
        }
        else {
            ModelDispatcher<Profile, PageMgr, List1, List2, Tail3>::dispatch4_3(mgr, root1, root2, root3, root4, functor);
        }
    }

    template <typename Handler>
    void dispatch4_4(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {
        if (root3->model_hash() == Head3::hash())
        {
            Head1 model1(*mgr);
            model1.root() = root1->id();

            Head2 model2(*mgr);
            model2.root() = root2->id();

            Head3 model3(*mgr);
            model3.root() = root3->id();

            Head4 model4(*mgr);
            model4.root() = root4->id();

            functor(mgr, model1, model2, model3, model4);
        }
        else {
            ModelDispatcher<Profile, PageMgr, List1, List2, List3, Tail4>::dispatch4_4(mgr, root1, root2, root3, root4, functor);
        }
    }


    static void BuildMetadataList(MetadataList &list) {
        Head1::Second::Init();
        Metadata* metadata = Head1::Second::reflection();
        list.push_back(metadata);
        ModelDispatcher<Profile, PageMgr, Tail1>::BuildMetadataList(list);
    }

    static void DestroyMetadata() {
    	ModelDispatcher<Profile, PageMgr, Tail1>::DestroyMetadata();
    }
};


template <
        typename Profile,
        typename PageMgr,
        typename List2,
        typename List3,
        typename List4
>
class ModelDispatcher<Profile, PageMgr, NullType, List2, List3, List4> {

    typedef typename PageMgr::Page                                              Page;

public:
    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch2_2(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_4(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    static void BuildMetadataList(MetadataList &list) {}
    static void DestroyMetadata() {}
};



template <
        typename Profile,
        typename PageMgr,
        typename List1,
        typename List3,
        typename List4
>
class ModelDispatcher<Profile, PageMgr, List1, NullType, List3, List4> {

    typedef typename PageMgr::Page                                              Page;

public:
    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch2_2(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_4(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    static void BuildMetadataList(MetadataList &list) {}
    static void DestroyMetadata() {}
};



template <
        typename Profile,
        typename PageMgr,
        typename List1,
        typename List2,
        typename List4
>
class ModelDispatcher<Profile, PageMgr, List1, List2, NullType, List4> {

    typedef typename PageMgr::Page                                              Page;

public:
    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch2_2(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_4(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    static void BuildMetadataList(MetadataList &list) {}
    static void DestroyMetadata() {}
};


template <
        typename Profile,
        typename PageMgr,
        typename List1,
        typename List2,
        typename List3
>
class ModelDispatcher<Profile, PageMgr, List1, List2, List3, NullType> {

    typedef typename PageMgr::Page                                              Page;

public:
    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch2_2(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_4(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    static void BuildMetadataList(MetadataList &list) {}
    static void DestroyMetadata() {}
};

template <
        typename Profile,
        typename PageMgr
>
class ModelDispatcher<Profile, PageMgr, NullType, NullType, NullType, NullType> {

    typedef typename PageMgr::Page                                              Page;

public:
    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch2_2(PageMgr *mgr, Page *root1, Page *root2, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch3_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Handler &functor) {}

    template <typename Handler>
    void dispatch(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_2(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_3(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    template <typename Handler>
    void dispatch4_4(PageMgr *mgr, Page *root1, Page *root2, Page *root3, Page *root4, Handler &functor) {}

    static void BuildMetadataList(MetadataList &list) {}

    static void DestroyMetadata() {}
};



template <typename List> class PageInitDispatcher;

template <typename Head, typename Tail>
class PageInitDispatcher<TypeList<Head, Tail> > {
public:
    static void BuildMetadataList(MetadataList &list) {
        Head::Init();
        list.push_back(Head::reflection());
        PageInitDispatcher<Tail>::BuildMetadataList(list);
    }

    static void DestroyMetadata() {
    	PageInitDispatcher<Tail>::DestroyMetadata();
    }
};

template <>
class PageInitDispatcher<NullType> {
public:
    static void BuildMetadataList(MetadataList &list) {}

    static void DestroyMetadata() {}
};


}
}

#undef CUBEFS_SOURCE

#endif	// _CUBEFS_CORE_CONTAINER_DISPATCHER_HPP
